// 这里存放公共的东西
#pragma once
#include <iostream>
#include <vector>
#include <time.h>
#include <thread>
#include <mutex>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
#include <stdint.h>

using std::cout;
using std::endl;

static const size_t MAX_BYTES = 256 * 1024;
// 哈希桶最大数量
static const size_t NFREELIST = 208;

// 现在你可以在你的代码中使用 PAGE_ID 来表示页面标识符了
typedef uintptr_t PAGE_ID; // 在Linux系统性就可以这样控制内存页了
// or
//  #if __WORDSIZE == 64
//      typedef unsigned long long PAGE_ID;
//  #else
//      typedef size_t PAGE_ID;
//  #endif
/*********************************/
// #ifdef _WIN64 //在windows系统性就可以这样控制内存页了
//     typedef unsigned long long PAGE_ID;
// #elif _WIN32
//     typedef size_t PAGE_ID;
// #endif
static void *&NextObj(void *obj)
{
    return *(void **)obj;
}
// 管理切分好的小对象的自由链表
class FreeList
{
public:
    void Push(void *obj)
    {
        // 头插
        // 取头四个字节，指向第一个
        NextObj(obj) = _freeList;
        // 然后自己变成第一个
        _freeList = obj;
    }
    void *Pop()
    {
        assert(_freeList);
        void *obj = _freeList;
        _freeList = NextObj(obj);
        return obj;
    }
    bool Empty()
    {
        return _freeList == nullptr;
    }
    size_t& MaxSize()
    {
        return _maxSize;
    }
private:
    void *_freeList;
    size_t _maxSize=1;
};
// 计算对象大小的对齐映射规则
class SizeClass
{
public:
    // 最开始以8对齐是考虑64位机器
    //  整体控制在最多10%左右的内碎片浪费
    //  [1,128] 8byte对齐 freelist[0,16)
    //  [128+1,1024] 16byte对齐 freelist[16,72)
    //  [1024+1,8*1024] 128byte对齐 freelist[72,128)
    //  [8*1024+1,64*1024] 1024byte对齐 freelist[128,184)
    //  [64*1024+1,256*1024] 8*1024byte对齐 freelist[184,208)
    static inline size_t _RoundUp(size_t size, size_t AilgnNUm /*对齐数*/)
    {
        /* size_t alignaSize;
         if(size%AilgnNUm!=0)
         {
             alignaSize =(size/AilgnNUm+1)*AilgnNUm;
         }
         else
         {
             alignaSize = size;
         }
         return alignaSize;*/
        return ((size + AilgnNUm - 1) & ~(AilgnNUm - 1));
    }
    // 对齐大小计算
    static inline size_t RoundUp(size_t bytes)
    {
        if (bytes <= 128)
        {
            return _RoundUp(bytes, 8);
        }
        else if (bytes <= 1024)
        {
            return _RoundUp(bytes, 16);
        }
        else if (bytes <= 8 * 1024)
        {
            return _RoundUp(bytes, 128);
        }
        else if (bytes <= 64 * 1024)
        {
            return _RoundUp(bytes, 1024);
        }
        else if (bytes <= 256 * 1024)
        {
            return _RoundUp(bytes, 8 * 1024);
        }
        else
        {
            assert(false);
            return -1;
        }
    }
    static inline size_t _Index(size_t bytes, size_t align_shift)
    {
        return ((bytes + (1 << align_shift) - 1) >> align_shift) - 1;
    }
    // 计算映射的哪一个自由链表桶
    static inline size_t Index(size_t bytes)
    {
        assert(bytes <= MAX_BYTES);
        // 每个区间有多少个链
        static int group_array[4] = {16, 56, 56, 56};
        if (bytes <= 128)
        {
            return _Index(bytes, 3);
        }
        else if (bytes <= 1024)
        {
            return _Index(bytes - 128, 4) + group_array[0];
        }
        else if (bytes <= 8 * 1024)
        {
            return _Index(bytes - 1024, 7) + group_array[1] + group_array[0];
        }
        else if (bytes <= 64 * 1024)
        {
            return _Index(bytes - 8 * 1024, 10) + group_array[2] + group_array[1] + group_array[0];
        }
        else if (bytes <= 256 * 1024)
        {
            return _Index(bytes - 64 * 1024, 13) + group_array[3] +
                   group_array[2] + group_array[1] + group_array[0];
        }
        else
        {
            assert(false);
        }
        return -1;
    }

    // 一次threadcache从中心缓存获取多少个对象
    static size_t NumMoveSize(size_t size)
    {
        assert(size>0);//单个对象的大小
        // [2, 512]，一次批量移动多少个对象的(慢启动)上限值
        // 小对象一次批量上限高
        // 大对象一次批量上限低
        int num = MAX_BYTES / size;//256k/单个对象
        if (num < 2)
            num = 2;
        if (num > 512)
            num = 512;
        return num;
    }
};

// 管理多个连续页的大块内存跨度结构
struct Span
{
    // 32位程序下 2^32字节，假设每页2^13（8k），那就被分成2^19页
    // 64位 2^64 / 2^13 = 2^51 这就太多了
    // 所以不同的系统下，字节应该不一样
    PAGE_ID _pageId = 0; // 页号
    size_t _n = 0;       // 页数
    // 带头的双向链表
    Span *_next = nullptr;
    Span *_prev = nullptr;
    // 切好的小块内存，被分配给thread cache的计数
    size_t _useCount;
    void *_freeList; // 切好的小块内存的自由链表
};
// 双向带头的链表
class SPanList
{
public:
    SPanList()
    {
        _head = new Span;
        _head->_next = _head;
        _head->_prev = _head;
    }
    void Insert(Span *pos, Span *newSpan)
    {
        assert(pos);
        assert(newSpan);
        Span *prev = pos->_prev;
        newSpan->_next = pos;
        pos->_prev = newSpan;
    }
    void Erase(Span *pos) // 这里只是从list中解掉，并没有将内存回收
    {
        assert(pos);
        assert(pos != _head);
        Span *prev = pos->_prev;
        Span *next = pos->_next;

        prev->_next = next;
        next->_prev = prev;
    }

private:
    Span *_head;
    // 桶锁
    std::mutex _mtx;
};